﻿using System;
using System.Collections.Generic;
using System.Linq;
using Flame.Dlr;
using System.Reflection;

namespace Flame.Reflection
{
    public class Intellisense
    {
        AllNamespaceAndType _allnamespaceAndType;
        int _startingWithCount = 0;
        public class Result
        {
            public List<IntellisenseItem> List { get; set; }
            public int PartialLength { get; set; }
            public int StartingWithCount { get; set; }
        }

        static Intellisense _istance  = null;
        public static Intellisense Instance
        {
            get
            {
                if (_istance == null) _istance = new Intellisense();
                return _istance;
            }
        }
        
        bool parsing(string name, string spartialvalue)
        {
            string nameLower = name.ToLower();
            string spartialvalueLower = spartialvalue.ToLower();
            if (nameLower.StartsWith(spartialvalueLower))
            {
                _startingWithCount++;
                return true;
            }
            return nameLower.Contains(spartialvalue.ToLower());
        }

        
        bool methodcondition(MethodInfo m, string spartialvalue, bool all )
        {
            return (m.IsPublic || all) && !m.IsSpecialName && parsing(m.Name,spartialvalue);
        }

        
        bool fieldcondition(FieldInfo m, string spartialvalue, bool all)
        {
            return (m.IsPublic || all) && !m.IsSpecialName && parsing(m.Name, spartialvalue);
        }

        
        
        IntellisenseItem namespaceOrClass(Class c, string spartialvalue)
        {
            string name = c.Name;
            if (!parsing(name, spartialvalue)) return null;
            if (c is Namespace)
            {
                //uu// implementare un sistema piu generico di separatore
                return new IntellisenseItem() { Name = name, Type = IntellisenseItemType.Namespace };
            }
            else
            {
                return new IntellisenseItem() { Name = name, Type = IntellisenseItemType.Type };
            }
        }

        
        IEnumerable<IntellisenseItem> types(Type t, string spartialvalue)
        {
            return (from m in t.GetNestedTypes() where parsing(m.Name, spartialvalue) select new IntellisenseItem() { Name = m.Name, Type = IntellisenseItemType.Type }).Distinct<IntellisenseItem>();
        }
        
        IEnumerable<IntellisenseItem> methods(Type t, string spartialvalue, bool all)
        {
            BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
            return (from m in t.GetMethods(flags) where methodcondition(m, spartialvalue, all) select new IntellisenseItem() { Name = m.Name, Type = IntellisenseItemType.Method }).Distinct<IntellisenseItem>();
        }
        
        
        IEnumerable<IntellisenseItem> events(Type t, string spartialvalue)
        {
            return (from m in t.GetEvents() where parsing(m.Name, spartialvalue) select new IntellisenseItem() { Name = m.Name, Type = IntellisenseItemType.Event }).Distinct<IntellisenseItem>();
        }
        
        IEnumerable<IntellisenseItem> properties(Type t, string spartialvalue)
        {
            return (from m in t.GetProperties() where parsing(m.Name, spartialvalue) select new IntellisenseItem() { Name = m.Name, Type = IntellisenseItemType.Property }).Distinct<IntellisenseItem>();
        }

        IEnumerable<FieldInfo> getAllFields(Type t)
        {
            if (t == null)
                return Enumerable.Empty<FieldInfo>();

            BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
            return t.GetFields(flags).Union(getAllFields(t.BaseType));
        }
        
        
        IEnumerable<IntellisenseItem> fields(Type t, string spartialvalue, bool all)
        {
            return (from m in getAllFields(t) where fieldcondition(m, spartialvalue, all) select new IntellisenseItem() { Name = m.Name, Type = IntellisenseItemType.Field }).Distinct<IntellisenseItem>();
        }


        List<MethodInfo> _getMethodsInfo(string text, IIntellisense compiler, out string spartialvalue)
        {
            List<MethodInfo> methods = new List<MethodInfo>();
            var r =   compiler.GetMatchResults(text);
            spartialvalue = r.Partialname;
            if (r.Type != IntellisenseResult.ResultType.InsideMethod) return methods;

            Type t = compiler.GetType(r.FullName);
            if (t != null)
            {
                System.Reflection.MethodInfo[] mis = t.GetMethods();
                foreach (System.Reflection.MethodInfo mi in mis)
                {
                    if (mi.Name == r.Partialname) methods.Add(mi);
                }
            }

            return methods;
        }

        Type _getType(string text, IIntellisense compiler, out string spartialvalue)
        {
            var r = compiler.GetMatchResults(text);
            spartialvalue = r.Partialname;

            if (r.Type == IntellisenseResult.ResultType.InsideMethod) return null;

            return compiler.GetType(r.FullName);
        }

        Intellisense()
        {
            _allnamespaceAndType = new AllNamespaceAndType();
                 }


        // ste
      /*  public void GetMatchResults(string text, out string svalue, out string spartialvalue)
        {
            if (text.Contains('.') || text.Contains(':'))
                IntellisenseParsing.Type.GetMatchResults(text, out svalue, out spartialvalue);
            else
            {
                svalue = "";
                spartialvalue = text;
            }
            
        }*/

            
        public Result GetListOfNames(string text, IIntellisense intellisense)
        {
            _startingWithCount = 0;
            List<IntellisenseItem> listOfNames = null;
            var  r= intellisense.GetMatchResults(text);

            if (r.Type == IntellisenseResult.ResultType.InsideMethod) return null;

            var namesNotEmpty = from s in r.Names where s != "" select s;

            List<Class> list = _allnamespaceAndType.AvailableNames(namesNotEmpty);
            if (list == null) return null;
                listOfNames = (from s in (from c in list select namespaceOrClass(c, r.Partialname)) where s != null select s).ToList<IntellisenseItem>();

                return new Result() { List = listOfNames, PartialLength = r.Partialname.Length, StartingWithCount = _startingWithCount };
        }

        public Result GetListMethodsInfo(string text, IIntellisense compiler)
        {
            if (compiler == null) return null;
            _startingWithCount = 0;
            string spartialvalue;
            List<MethodInfo> methods = _getMethodsInfo(text, compiler, out spartialvalue);

            if (methods.Count == 0) return null;
            return new Result() { List = (from m in methods select new IntellisenseItem(){ Name =  m.ToString(), Type = IntellisenseItemType.Method}).ToList(), PartialLength = spartialvalue.Length, StartingWithCount = _startingWithCount };
        }

        public Result GetListOfMembers(string text, IIntellisense compiler, bool publiconly = true)
        {
            _startingWithCount = 0;
            List<IntellisenseItem> listOfNames = null;
            string spartialvalue = "";
            Type t = _getType(text, compiler , out spartialvalue);
            //uu// "RubyModule"??
            if (t != null && t.Name != "RubyModule")
            {
                listOfNames = methods(t, spartialvalue, !publiconly).ToList<IntellisenseItem>();
                listOfNames = listOfNames.Concat<IntellisenseItem>(fields(t, spartialvalue, !publiconly)).ToList<IntellisenseItem>();
                listOfNames = listOfNames.Concat<IntellisenseItem>(events(t, spartialvalue)).ToList<IntellisenseItem>();
                listOfNames = listOfNames.Concat<IntellisenseItem>(properties(t, spartialvalue)).ToList<IntellisenseItem>();
                listOfNames = listOfNames.Concat<IntellisenseItem>(types(t, spartialvalue)).ToList<IntellisenseItem>();
            }
            if (listOfNames == null) return null;
            return new Result() { List = listOfNames, PartialLength = spartialvalue.Length, StartingWithCount = _startingWithCount };
        }
    }


   /* class xxx
    {
        int _startingWithCount = 0;
        bool parsing(string name, string spartialvalue)
        {
            string nameLower = name.ToLower();
            string spartialvalueLower = spartialvalue.ToLower();
            if (nameLower.StartsWith(spartialvalueLower))
            {
                _startingWithCount++;
                return true;
            }
            return nameLower.Contains(spartialvalue.ToLower());
        }


        IEnumerable<IntellisenseItem> types(Type t, string spartialvalue)
        {
            return (from m in t.GetNestedTypes() where parsing(m.Name, spartialvalue) select new IntellisenseItem() { Name = m.Name, Type = IntellisenseItemType.Type }).Distinct<IntellisenseItem>();
        }

        IEnumerable<IntellisenseItem> methods(Type t, string spartialvalue, bool all)
        {
            BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
            return (from m in t.GetMethods(flags) where methodcondition(m, spartialvalue, all) select new IntellisenseItem() { Name = m.Name, Type = IntellisenseItemType.Method }).Distinct<IntellisenseItem>();
        }


        IEnumerable<IntellisenseItem> events(Type t, string spartialvalue)
        {
            return (from m in t.GetEvents() where parsing(m.Name, spartialvalue) select new IntellisenseItem() { Name = m.Name, Type = IntellisenseItemType.Event }).Distinct<IntellisenseItem>();
        }

        IEnumerable<IntellisenseItem> properties(Type t, string spartialvalue)
        {
            return (from m in t.GetProperties() where parsing(m.Name, spartialvalue) select new IntellisenseItem() { Name = m.Name, Type = IntellisenseItemType.Property }).Distinct<IntellisenseItem>();
        }

        List<IntellisenseItem> listOfNames(Type t, string spartialvalue, bool publiconly = true)
        {
            List<IntellisenseItem> listOfNames;

            listOfNames = methods(t, spartialvalue, !publiconly).ToList<IntellisenseItem>();
                listOfNames = listOfNames.Concat<IntellisenseItem>(fields(t, spartialvalue, !publiconly)).ToList<IntellisenseItem>();
                listOfNames = listOfNames.Concat<IntellisenseItem>(events(t, spartialvalue)).ToList<IntellisenseItem>();
                listOfNames = listOfNames.Concat<IntellisenseItem>(properties(t, spartialvalue)).ToList<IntellisenseItem>();
                listOfNames = listOfNames.Concat<IntellisenseItem>(types(t, spartialvalue)).ToList<IntellisenseItem>();

                return listOfNames;
        }
    }*/
}
